#pragma once

#include <brpc/server.h>
#include <log.h>
#include <memory>
#include <odb/database.hxx>
#include <odb/mysql/database.hxx>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <chrono>
#include <ctime>

#include "Blog.hpp"
#include "Blog-odb.hxx"
#include "BlogOperator.hpp"
#include "UserOperator.hpp"
#include "Util.hpp"
#include "blog.pb.h"
#include "es.h"
#include "es_blog.h"
#include "etcd.hpp"
#include "etcd_path.h"
#include "user.pb.h"
#include "mysql.h"
#include "redis.h"
#include "kafka_producer.hpp"
#include "user_action.pb.h"
#include "user_redis.h"


namespace lyt{
    class BlogServiceImpl : public blog::BlogService{
    private:
        // blogdata mysql
        BlogData::ptr _blog_mysql;
        UserData::ptr _user_mysql;
        // redis
        UserRedis::ptr _user_redis;
        // kafka
        KafkaProducer::ptr _kafka_producer;
        // es
        EsBlogData::ptr _esclient;
    private:
        void SetStats(const std::string& email,user::UserStats& stats){
            if(email.empty()){
                debuglog << "邮箱为空!";
                return;
            }
            User user;
            _user_mysql->query_user(email, user);
            stats.set_email(user.email);
            stats.set_avatar(user.avatar);
            stats.set_name(user.username);
        }
        // T可能是Comment，可能是Blog
        template<typename Container>
        void SetStatsMap(const Container& info,std::unordered_map<std::string, user::UserStats>& userstats_map){
            if(info.empty()){
                debuglog << "info is empty";
            }
            // 自动去重
            std::unordered_set<std::string> authors_id;
            for(const auto& e:info){
                authors_id.insert(e.author_id);
            }
            // 一次性查表
            _user_mysql->query_userstats_by_idxs(authors_id,userstats_map);
        }
    public:
        using ptr = std::shared_ptr<BlogServiceImpl>;
        BlogServiceImpl(const std::shared_ptr<odb::mysql::database>& odb
                        ,const std::shared_ptr<sw::redis::Redis> redis
                        ,const std::shared_ptr<KafkaProducer> kafkaproducer
                        ,const std::shared_ptr<esclient> esclient)
                    :_blog_mysql(std::make_shared<BlogData>(odb)),
                    _user_mysql(std::make_shared<UserData>(odb)),
                    _user_redis(std::make_shared<UserRedis>(redis)),
                    _kafka_producer(kafkaproducer),
                    _esclient(std::make_shared<EsBlogData>(esclient))
        {

            _esclient->index();
        }
        void BlogList(::google::protobuf::RpcController* controller,
                        const ::blog::BlogListRequest* request,
                        ::blog::BlogListResponse* response,
                        ::google::protobuf::Closure* done){
            /*
            1. 验证token
            2. 根据页码和大小获取博客信息
            3. 构建响应
            */
            brpc::ClosureGuard done_guard(done);
            auto handler = [&](const bool success,const std::string& message=""){
                response->set_success(success);
                response->set_message(message);
                return;
            };
            auto opt = _user_redis->GetUserId(request->token());   
            if(!opt){
                controller->SetFailed("Invalid token");
                return handler(false,"获取博客列表失败，用户未登录");
            } 
            std::string email = *opt;
            std::vector<class Blog> info;

            if(!_blog_mysql->query_blogs(request->page(), request->page_size(), "",true,info)){
                controller->SetFailed("failed to query_blogs");
                return handler(false,"无法查询博客");
            }
            // 避免N次重复查询问题，要一次查出来所有的userstats和author_id构建映射，防止每次循环查询一次表，效率低下
            std::unordered_map<std::string,user::UserStats> userstats_map; 
            SetStatsMap(info, userstats_map);

            for(auto& blog:info){
                blog::BlogPreview* preview = response->add_blogs();
                preview->set_id(to_string(blog.id));
                preview->set_title(blog.title);
                preview->set_date(blog.date);
                preview->set_preview(blog.preview);
                preview->set_view_count(blog.view_count);
                preview->set_likes(blog.likes);
                // preview->mutable_author()会返回一个 UserStats* 指针，指向 preview 内部的 author 字段
                *preview->mutable_author() = userstats_map[blog.author_id];
            }
            response->set_success(true);
            response->set_message("获取成功~");
            response->set_total(info.size());
        }
        void UserBlogList(::google::protobuf::RpcController* controller,
                        const ::blog::UserBlogListRequest* request,
                        ::blog::BlogListResponse* response,
                        ::google::protobuf::Closure* done){
            /*
            1. 验证token
            2. publishes字段如果为true,那就查询当前用户已发表的文章,为false就查询草稿
            3. 构建响应
            */
            brpc::ClosureGuard done_guard(done);
            auto handler = [&](const bool success,const std::string& message=""){
                response->set_success(success);
                response->set_message(message);
                return;
            };
            auto opt = _user_redis->GetUserId(request->token());   
            if(!opt){
                controller->SetFailed("Invalid token");
                return handler(false,"获取博客列表失败，用户未登录");
            } 
            std::string email = *opt;
            std::vector<class Blog> info;

            if(!_blog_mysql->query_blogs(request->page(), request->page_size(), email,request->published(),info)){
                controller->SetFailed("failed to query_blogs");
                return handler(false,"无法查询博客");
            }
            // 避免N次重复查询问题，要一次查出来所有的userstats和author_id构建映射，防止每次循环查询一次表，效率低下
            std::unordered_map<std::string,user::UserStats> userstats_map; 
            SetStatsMap(info, userstats_map);

            for(auto& blog:info){
                blog::BlogPreview* preview = response->add_blogs();
                preview->set_id(to_string(blog.id));
                preview->set_title(blog.title);
                preview->set_date(blog.date);
                preview->set_preview(blog.preview);
                preview->set_view_count(blog.view_count);
                preview->set_likes(blog.likes);
                // preview->mutable_author()会返回一个 UserStats* 指针，指向 preview 内部的 author 字段
                *preview->mutable_author() = userstats_map[blog.author_id];
            }
            response->set_success(true);
            response->set_message("获取成功~");
            response->set_total(info.size());
        }
        void BLOGDetail(::google::protobuf::RpcController* controller,
                        const ::blog::BlogDetailRequest* request,
                        ::blog::BlogDetailResponse* response,
                        ::google::protobuf::Closure* done){
            /*
            1. 验证token
            2. 根据文章id查询对应文章
            3. 根据page和page_size筛选出显示几条评论（如果文章评论多的话需要分页显示，太多就会导致网络拥塞）
            4. 更新Blog表中的view_count（浏览量）
            5. 构建响应
            */
            // 1.
            brpc::ClosureGuard done_guard(done);
            auto handler = [&](const bool success,const std::string& message=""){
                response->set_success(success);
                response->set_message(message);
                return;
            };
            auto opt = _user_redis->GetUserId(request->token());   
            if(!opt){
                debuglog << "Invalid token";
                controller->SetFailed("Invalid token");
                return handler(false,"获取博客详情失败，用户未登录");
            }
            std::string email = *opt;

            // 2.
            // 这个BlogDetail是mysql中的表，最后要的是protobuf中的BlogDetail
            Blog blogdetail;
            if(!_blog_mysql->query_blog(stoi(request->id()), blogdetail)){
                controller->SetFailed("failed to query_blogdetail");
                return handler(false,"无法查询博客");
            }
            // 3.
            // 把Userstats查出来
            user::UserStats stats;
            SetStats(blogdetail.author_id, stats);
            // 把Comment查出来,同样的，这个是mysql中的Comment，最后要的是protobuf中的Comment
            std::vector<class Comment> info;
            // if(!_blog_mysql->query_comments_by_time(stoi(request->id()),request->page(), request->page_size(), info)){
            //     controller->SetFailed("failed to query_blogcomments_by_page");
            //     return handler(false,"无法查询博客");
            // }
            if(!_blog_mysql->query_comments_by_page(stoi(request->id()),0,request->page(), request->page_size(), info)){
                controller->SetFailed("failed to query_blogcomments_by_page");
                return handler(false,"无法查询博客");
            }
            // 设置retblogdetail
            blog::BlogDetail retblogdetail;
            retblogdetail.set_id(to_string(blogdetail.id));
            retblogdetail.set_title(blogdetail.title);
            *retblogdetail.mutable_author()=stats;
            retblogdetail.set_date(blogdetail.date);
            retblogdetail.set_content(blogdetail.content);
            retblogdetail.set_likes(blogdetail.likes);
            retblogdetail.set_comment_total(blogdetail.comment_total);

            // 避免N次重复查询问题
            std::unordered_map<std::string,user::UserStats> userstats_map; 
            SetStatsMap(info, userstats_map);

            // Comment表中存储的是author_id，要把它转化成UserStats存入retBlogDetail的评论列表中
            debuglog << "info.size:" << info.size();
            debuglog << "page:" << request->page();
            debuglog << "page_size:" << request->page_size();
            for(auto& e:info){
                blog::Comment* comment = retblogdetail.add_comments();
                comment->set_id(to_string(e.id));
                *comment->mutable_author() = userstats_map[e.author_id];
                comment->set_content(e.content);
                comment->set_created_at(e.created_at);
                comment->set_likes(e.likes);
                comment->set_parent_id(to_string(e.parent_id));
                comment->set_comment_total(e.comment_total);
            }
            // 4.
            _blog_mysql->update_blog_view_count(stoi(request->id()));

            // 构造响应
            *response->mutable_blog() = retblogdetail;
            handler(true,"获取详情成功~");
        }
        void Like(::google::protobuf::RpcController* controller,
                        const ::blog::LikeRequest* request,
                        ::blog::LikeResponse* response,
                        ::google::protobuf::Closure* done){
            /*
            1. 验证token
            2. 实现查询Like表中是不是已经有数据（同一个用户不能对同一个博客二次点赞）
            3. 更新点赞数
            4. 查询作者id
            5. 更新like表
            6. 构造事件并序列化为消息体后发送给消息队列
            7. 构建响应
            */
            // 1. 验证token
            brpc::ClosureGuard done_guard(done);
            auto handler = [&](const bool success,const std::string& message=""){
                response->set_success(success);
                response->set_message(message);
                return;
            };

            auto now_time_t = GetTimestamp::gettimestamp();
            auto opt = _user_redis->GetUserId(request->token());   
            if(!opt){
                debuglog << "Invalid token";
                controller->SetFailed("Invalid token");
                return handler(false,"获取博客详情失败，用户未登录");
            }
            std::string email = *opt;
            // 2. 
            struct LikeKey likekey(stoi(request->blog_id()),email);
            if(_blog_mysql->query_like(likekey) == true){
                // 数据库中已经存在这个点赞记录了，无法继续点赞
                return handler(false,"已经很赞了，不能再赞了~");
            }

            // 3. 更新点赞数
            if(!_blog_mysql->update_blog_likes(stoi(request->blog_id()))){
                controller->SetFailed("failed to update_blog_likes");
                return handler(false,"点赞失败：数据库操作异常");
            }
            debuglog << "更新点赞";
            // 4. 查询作者id
            std::string author_id;
            if(!_blog_mysql->query_blog_author_id(stoi(request->blog_id()), author_id)){
                controller->SetFailed("failed to update_blog_likes");
                return handler(false,"点赞失败：数据库更新失败");
            }
            // 5. 更新like表
            class Like like(likekey,now_time_t);
            _blog_mysql->inset_like(like);


            // 6. 构造事件发送给消息队列
            analytics::BehaviorActionEvent event;
            event.set_event_type(analytics::LIKE);
            event.set_user_id(email);
            event.set_blog_id(request->blog_id());

            std::string payload;
            event.SerializeToString(&payload);
            if(!_kafka_producer->send(email, payload)){
                errorlog << "Kafka send failed";
            }
            // 7. 构建响应
            handler(true,"点赞成功~");
        }
        void Comment(::google::protobuf::RpcController* controller,
                        const ::blog::CommentRequest* request,
                        ::blog::CommentResponse* response,
                        ::google::protobuf::Closure* done){
            /*
            1. 验证token
            2. 更新Comment表以及Blog表中的comment_total字段
            3. 构建响应
            */

            brpc::ClosureGuard done_guard(done);
            auto handler = [&](bool success,std::string message=""){
                response->set_success(success);
                response->set_message(message);
                return;
            };

            // 1. 验证token
            auto opt = _user_redis->GetUserId(request->token());   
            if(!opt){
                debuglog << "Invalid token";
                controller->SetFailed("Invalid token");
                return handler(false,"评论失败，用户未登录");
            }
            std::string email = *opt;
            // 2. 准备初始化参数
            // std::string author_id;
            // if(!_blog_mysql->query_blog_author_id(stoi(request->blog_id()),author_id)){
            //     controller->SetFailed("failed to query_blog_author_id");
            //     return handler(false,"无法获取博客作者");
            // }
            unsigned long long parent_id;
            long long timestamp = GetTimestamp::gettimestamp();
            if(request->parent_id().empty()){
                parent_id = 0;
            }else{
                debuglog << "I m in";
                parent_id  = stoi(request->parent_id());
            }
            // 初始化Comment对象
            class Comment comment(stoi(request->blog_id()),email,request->content(),timestamp,parent_id);
            if(!_blog_mysql->insert_blogcomment(comment)){
                controller->SetFailed("failed to insert_blogcomment");
                return handler(false,"无法插入Comment");
            }
            if(parent_id!=0){
                debuglog << "parent_id!=0";
                if(!_blog_mysql->update_blog_comment_total(parent_id)){
                    controller->SetFailed("failed to update_blog_comment_total");
                    return handler(false,"无法更新博客");
                }
            }
            // 插入表中之后再查一下评论id，用于更新Comments表
            debuglog << "comment id:" << comment.id;
            // 更新Blog
            if(!_blog_mysql->update_blog_view_total(stoi(request->blog_id()))){
                controller->SetFailed("failed to insert_blogcomments");
                return handler(false,"无法插入Comments");
            }
            // 3. 构建响应
            blog::Comment blog_comment;
            blog_comment.set_id(to_string(comment.id));
            blog_comment.set_content(request->content());
            blog_comment.set_created_at(timestamp);
            blog_comment.set_likes(0);
            blog_comment.set_parent_id(request->parent_id());
            blog_comment.set_comment_total(0);

            user::UserStats userstats;
            SetStats(email,userstats);
            blog_comment.mutable_author()->CopyFrom(userstats);// userstats;
            debuglog << userstats.email();
            debuglog << userstats.name();
            debuglog << userstats.avatar();
            *response->mutable_comment() = blog_comment;
            handler(true,"点赞成功~");
        }
        void BlogComment(::google::protobuf::RpcController* controller,
            const ::blog::BlogCommentRequest* request,
            ::blog::BlogCommentResponse* response,
            ::google::protobuf::Closure* done){
            /*
            1. 验证token
            2. 根据parent_id，和page，page_size查询Comments表获取评论
            4. 构建响应
            */
            brpc::ClosureGuard done_guard(done);
            auto handler = [&](bool success,std::string message=""){
                response->set_success(success);
                response->set_message(message);
                return;
            };
            // 1. 验证token
            auto opt = _user_redis->GetUserId(request->token());   
            if(!opt){
                debuglog << "Invalid token";
                controller->SetFailed("Invalid token");
                return handler(false,"评论失败，用户未登录");
            }

            std::vector<class Comment> info;
            if(!_blog_mysql->query_comments_by_page(stoi(request->blog_id()), 
            stoi(request->parent_id()), request->page(), request->page_size(), info)){
                controller->SetFailed("failed to query_blogcomments_by_page");
                return handler(false,"无法查询评论");
            }

            // 避免N次重复查询
            std::unordered_map<std::string,user::UserStats> userstats_map; 
            SetStatsMap(info, userstats_map);

            // 查询出的只是数据库中的author_id，还需要构建protobuf中的userstats进行返回
            for(auto& cm : info){
                auto comment = response->add_comments();
                comment->set_id(to_string(cm.id));
                *comment->mutable_author() = userstats_map[cm.author_id];
                comment->set_content(cm.content);
                comment->set_created_at(cm.created_at);
                comment->set_likes(cm.likes);
                comment->set_comment_total(cm.comment_total);
            }
            // 构建响应
            response->set_total(info.size());
            handler(true,"查询成功~");
        }
        void DeleteBlog(::google::protobuf::RpcController* controller,
            const ::blog::DeleteBlogRequest* request,
            ::blog::DeleteBlogResponse* response,
            ::google::protobuf::Closure* done){
            /*
            1. 验证token
            2. 根据文章id删除Blog表中的行
            3. 根据blog_id在Comments中查询出所有的文章评论，删除（涉及Comment,Comments）
            4. 删除es中blog
            5. 构建响应
            */
            brpc::ClosureGuard done_guard(done);
            auto handler = [&](bool success,std::string message=""){
                response->set_success(success);
                response->set_message(message);
                return;
            };
            // 1. 验证token
            auto opt = _user_redis->GetUserId(request->token());   
            if(!opt){
                debuglog << "Invalid token";
                controller->SetFailed("Invalid token");
                return handler(false,"删除博客，用户未登录");
            }
            // 2. 根据文章id删除Blog表中的行
            if(!_blog_mysql->delete_blog(stoi(request->blog_id()))){
                controller->SetFailed("failed to delete_blog");
                return handler(false,"无法删除博客");
            }
            // 3. 根据blog_id在Comments中查询出所有的文章评论，删除（涉及Comment,Comments）
            if(!_blog_mysql->delete_comment(stoi(request->blog_id()))){
                controller->SetFailed("failed to delete_blogcomments");
                return handler(false,"无法删除博客");
            }
            // 4.删除es中blog
            _esclient->remove(request->blog_id());

            // 5. 构建响应
            handler(true,"删除成功~");
        }
        void CreateBlog(::google::protobuf::RpcController* controller,
            const ::blog::CreateBlogRequest* request,
            ::blog::CreateBlogResponse* response,
            ::google::protobuf::Closure* done){
            /*
            1. 验证token
            2. 根据blog_id判断是新建博客还是修改博客
            3. 构建一个sql中的表对象，然后填值，插入表中
            4. 插入es
            5. 构建响应
            */
            brpc::ClosureGuard done_guard(done);
            auto handler = [&](bool success,std::string message=""){
                response->set_success(success);
                response->set_message(message);
                return;
            };
            // 1. 验证token
            auto opt = _user_redis->GetUserId(request->token());   
            if(!opt){
                debuglog << "Invalid token";
                controller->SetFailed("Invalid token");
                return handler(false,"创建博客失败，用户未登录");
            }
            std::string email = *opt;
            // 3. 构建对象
            Blog blog;
            if(request->blog_id().empty()){
                // 新建博客
                blog.title = request->title();
                blog.author_id = email;
                blog.date = GetTimeISO::gettimeiso();
                blog.preview = CharacterExtraction::charextrac(request->content(), 100);
                blog.content = request->content();
                blog.published = request->published();
                if(!_blog_mysql->insert_blog(blog)){
                    controller->SetFailed("failed to insert blog");
                    return handler(false,"发表博客失败");
                }
            }else{
                // 更改博客
                if(!_blog_mysql->query_blog(stoi(request->blog_id()), blog)){
                    controller->SetFailed("failed to query blog");
                    handler(false,"查询博客失败");
                }
                blog.title = request->title();
                blog.content = request->content();
                blog.published = request->published();
                if(!_blog_mysql->update_blog(blog)){
                    controller->SetFailed("failed to update blog");
                    handler(false,"更新博客失败");
                }
            }
            // 4 插入es(有可能是插入新记录，或者是更新记录)
            // _insert->set_value("id", blog.id)
            //         .set_value("title", blog.title)
            //         .set_value("date", blog.date)
            //         .set_value("content", blog.content)
            //         .run("blog", blog.author_id);

            // kafka

            // 5
            handler(true,"发表博客成功~");
        }
        void EditBlog(::google::protobuf::RpcController* controller,
            const ::blog::EditBlogRequest* request,
            ::blog::EditBlogResponse* response,
            ::google::protobuf::Closure* done){
            /*
            1. 验证token
            2. 根据blog_id查询数据
            3. 构建响应
            */
            brpc::ClosureGuard done_guard(done);
            auto handler = [&](bool success,std::string message=""){
                response->set_success(success);
                response->set_message(message);
                return;
            };
            // 1. 验证token
            auto opt = _user_redis->GetUserId(request->token());   
            if(!opt){
                debuglog << "Invalid token";
                controller->SetFailed("Invalid token");
                return handler(false,"评论失败，用户未登录");
            }
            if(request->blog_id().empty()){
                return handler(false,"无法查找博客数据");
            }
            std::string email = *opt;
            //  2. 根据blog_id查询数据
            Blog blog;
            if(!_blog_mysql->query_blog(stoi(request->blog_id()), blog)){
                controller->SetFailed("failed to query blog");
                handler(false,"查询博客失败");
            }
            // 3.
            response->set_blog_id(request->blog_id());
            response->set_title(blog.title);
            response->set_content(blog.content);
            response->set_published(blog.published);
            handler(true,"编辑内容后，请重新发布~");
        }
        void LikedBlogList(::google::protobuf::RpcController* controller,
            const ::blog::LikedBlogListRequest* request,
            ::blog::LikedBlogListResponse* response,
            ::google::protobuf::Closure* done){
            /*
            1. 验证token
            2. 现在Like表中查询出博客id，用于后续查询BlogPreview
            3. 构建响应
            */
            debuglog << "In LikedBlogList";
            brpc::ClosureGuard done_guard(done);
            auto handler = [&](bool success,std::string message=""){
                response->set_success(success);
                response->set_message(message);
                return;
            };
            // 1. 验证token
            auto opt = _user_redis->GetUserId(request->token());   
            if(!opt){
                debuglog << "Invalid token";
                controller->SetFailed("Invalid token");
                return handler(false,"评论失败，用户未登录");
            }

            // 2.
            std::vector<unsigned long long> blog_ids;
            if(!_blog_mysql->query_like_by_page(request->email(), request->page(), request->page_size(), blog_ids)){
                controller->SetFailed("Invalid token");
                return handler(false,"查询失败，用户未登录");
            }
            // 查询博客信息
            std::vector<Blog> blogs;
            if (!_blog_mysql->query_blog_by_ids(blog_ids, blogs)) {
                controller->SetFailed("查询失败");
                return handler(false, "查询博客详情失败");
            }
            std::unordered_map<std::string, user::UserStats> userstats_map;
            SetStatsMap(blogs, userstats_map);
            // 查询数组中对应的博客预览
            for(auto& blog : blogs){
                auto preview = response->add_blogs();
                preview->set_id(to_string(blog.id));
                preview->set_title(blog.title);
                preview->set_date(blog.date);
                preview->set_view_count(blog.view_count);
                preview->set_likes(blog.likes);

                *preview->mutable_author() = userstats_map[blog.author_id];
            }
            response->set_total(blog_ids.size());
            handler(true,"查询成功~");
        }
        void DeleteLikeBlog(::google::protobuf::RpcController* controller,
            const ::blog::DeleteLikeBlogRequest* request,
            ::blog::DeleteLikeBlogResponse* response,
            ::google::protobuf::Closure* done){
            /*
            1. 验证token
            2. 删除Like表中的对应数据
            3. 更新Blog表中的获赞数和user表中的获赞数（通过消息队列）
            4. 构建响应
            */
            brpc::ClosureGuard done_guard(done);
            auto now_time_t = GetTimestamp::gettimestamp();
            auto handler = [&](bool success,std::string message=""){
                response->set_success(success);
                response->set_message(message);
                return;
            };
            // 1. 验证token
            auto opt = _user_redis->GetUserId(request->token());   
            if(!opt){
                debuglog << "Invalid token";
                controller->SetFailed("Invalid token");
                return handler(false,"评论失败，用户未登录");
            }
            std::string email = *opt;
            // 2.
            struct LikeKey likekey(stoi(request->blog_id()),email);
            if(!_blog_mysql->delete_like(likekey)){
                controller->SetFailed("Invalid token");
                return handler(false,"取消点赞失败");
            }
            // 3.
            // 先更新Blog表
            if(!_blog_mysql->delete_blog_like(stoi(request->blog_id()))){
                controller->SetFailed("failed to delete_blog_like");
                return handler(false,"取消点赞失败");
            }
            // 构造事件发送给消息队列
            analytics::BehaviorActionEvent event;
            // 获取博客作者id
            std::string author_id;
            if(!_blog_mysql->query_blog_author_id(stoi(request->blog_id()), author_id)){
                controller->SetFailed("failed to query_blog_author_id");
                return handler(false,"无法获取博客作者");
            }
            event.set_event_type(analytics::UNLIKE);
            event.set_user_id(email);
            event.set_blog_id(request->blog_id());
            std::string payload;
            event.SerializeToString(&payload);
            if(!_kafka_producer->send(email, payload)){
                errorlog << "Kafka send failed";
                controller->SetFailed("kafka can't send task!");
                return handler(false,"取消点赞失败!");
            }
            handler(true,"取消点赞成功~");
        }
    };

    // class BlogServerBuild;
    class BlogServer{
    private:
        std::shared_ptr<brpc::Server> _server;
        lyt::Register::ptr _register;
        BlogServiceImpl::ptr _blog;
    public:
        // friend class BlogServerBuild;
        using ptr = std::shared_ptr<BlogServer>;
        BlogServer(const std::shared_ptr<brpc::Server>& svr,
                const BlogServiceImpl::ptr& blog,
                const lyt::Register::ptr& regis
                )
                :_server(svr),_register(regis),_blog(blog){}
        void start(){
            _server->RunUntilAskedToQuit();
        }
    };

    class BlogServerBuild{
    private:
        std::shared_ptr<odb::mysql::database> _odb;
        std::shared_ptr<sw::redis::Redis> _redis;
        std::shared_ptr<brpc::Server> _server;
        BlogServiceImpl::ptr _blog;
        lyt::Register::ptr _register;
        KafkaProducer::ptr _kafka_producer;
        esclient::ptr _esclient;

    public:

        using ptr = std::shared_ptr<BlogServerBuild>;
        BlogServerBuild& make_odb_client(
                const std::string& user,
                const std::string& passwd,
                const std::string& db_name,
                const std::string& host,
                size_t port,
                const std::string& charset,
                size_t conn_pool_num){
                    _odb = lyt::mysql_build::build(user, passwd, db_name, host, port, charset, conn_pool_num);
                    return *this;
        }
        BlogServerBuild& make_redis_client(const std::string& host,int port, int db_id,bool keepalive = true) {
            _redis = redis_build::build(host, port, db_id,keepalive);
            return *this;
        }
        BlogServerBuild& make_kafka_producer(const std::string& brokers,const std::string& topic_name){
            _kafka_producer = std::make_shared<KafkaProducer>(brokers,topic_name);
            return *this;
        }
        BlogServerBuild& make_elasticsearch_client(const std::vector<std::string>& host){
            _esclient = std::make_shared<esclient>(host);
            return *this;
        }
        BlogServerBuild& make_blog_service() {
            if (!_odb) {
                errorlog << "failed to odb uninitizalier";
                abort();
            }
            if (!_redis) {
                errorlog << "failed to redis uninitizalier";
                abort();
            }
            if(!_kafka_producer){
                errorlog << "failed to kafka uninitizalier";
                abort();
            }
            if(!_esclient){
                errorlog << "failed to esclient uninitizalier";
                abort();
            }
            _blog = std::make_shared<BlogServiceImpl>(_odb,_redis,_kafka_producer,_esclient);
            return *this;
        }

        BlogServerBuild& make_brpc_server(const std::size_t& port) {
            if(!_blog){
                errorlog << "UserService uninitizalier";
                abort();
            }
            _server = std::make_shared<brpc::Server>();
            if(_server->AddService(&(*_blog),brpc::SERVER_DOESNT_OWN_SERVICE) != 0){
                errorlog << "Failed to add service: UserService";
                abort();
            }
            brpc::ServerOptions options;
            options.idle_timeout_sec = -1;
            if(_server->Start(port,&options) != 0){
                errorlog << "Failed to start service: UserService";
                abort();
            }
            return *this;
        }
        BlogServerBuild& make_register(const std::string& host,const std::string& value) {
            if(!_server){
                errorlog << "Can't register to etcd,because UserService not start";
                abort();
            }
            _register = std::make_shared<lyt::Register>(host);
            if(!_register->registory(lyt::blog_path + value, value)){
                errorlog << "Fail register UserService to etcd";
                abort();
            }
            return *this;
        }

        BlogServer::ptr build() {
            if (!_server) {
                errorlog << "failed to brpc uninitizalier";
                abort();
            }
            if (!_blog) {
                errorlog << "failed to user uninitizalier";
                abort();
            }
            if (!_register) {
                errorlog << "failed to register uninitizalier";
                abort();
            }
            return std::shared_ptr<BlogServer>(new BlogServer(_server,_blog,_register));
        }
    };
}

